16
תגובות

Multiple Inheritance ב-PHP (שוב)

פתח OrelBeY ,
הפעם אני מעוניין בעזרתכם במשהו שנתקלתי בו.
נניח שיש לי את המחלקות הבאות: מרובע, מקבילית, דלתון. גם מקבילית וגם דלתון אמורים לרשת ממרובע. חשבתי על שימוש ב-Traits, אבל זה סתם פתרון לא נקי ולא כיף (לזה, בכל אופן). למישהו יש רעיון?

16 תשובות

avatar ענה intval ב 13 לפברואר 2013 #

אין שום בעיה שכמה קלאסים ירשו מקלאס אחד או לירוש מקלאס שיורש ממשהו אחר.

class Quadrilateral {} // מרובע
class Rectangle extends Quadrilateral {} // מלבן
class Square extends Rectangle {} // ריבוע
class Kite extends Quadrilateral {} // דלתון

avatar ענה OrelBeY ב 13 לפברואר 2013 #

אופס. התבלבלתי.
התכוונתי למקרה כמו הבא:
ריבוע יורש ממרובע, ממקבילית, ממלבן, מדלתון וממעוין. איך אני אמור לעשות את זה? (בסופו של דבר הוא עדיין צריך לרשת מכמה מחלקות, גם אם חלקם יורשים ממחלקות אחרות, כמו ממרובע.)

avatar ענה intval ב 13 לפברואר 2013 #

זה לא דוגמה טובהבגלל

שריבוע יורש רק ממקבילית
מקבילית יורשת ממלבן
מלבן יורש ממרובע.

ובסופו של דבר יוצא שריבוע הוא גם מקבילית, גם מרובע וגם מלבן. והקוד למעלה מתאר בדיוק את המצב הזה.

היית יכול ללקת דוגמה יותר מסובכת ולקחת סוס שיכול לתפקד אצלך גם בתור חיה וגם בתור כלי תחבורה.
ושם הפתרון הוא באמת traits.
אבל הסיבה שבדוגמה עם המצולעים traits מרגישים לך מוזר, זה בגלל שפה אין לך באמת ירושה כפולה.
רק תשנה את הסדר של האיררכיה והכל יסתדר.

avatar ענה OrelBeY ב 13 לפברואר 2013 #

מלבן יורש ממקבילית, ולא מקבילית ממלבן. :)
וזה כן יוצא שהוא צריך יותר מאחד. אני בדיוק לומד את זה במתמטיקה. XD

avatar ענה intval ב 13 לפברואר 2013 #

אז תחליף אותם במקום.

מקבילית יורשת ממרובע
מלבן יורש ממקבילית
ריבוע יורש ממלבן

אני עדיין לא רואה מה לא מסתדר.

avatar ענה OrelBeY ב 13 לפברואר 2013 #

בסופו של דבר ריבוע (בתור דוגמה) צריך לרשת מכמה: גם ממעוין וגם ממלבן (ונראה לי גם מדלתון, אבל זה לא משנה כרגע).

- מקבילית יורשת ממרובע
-- מלבן יורש ממקבילית
-- מעוין יורש ממקבילית
=> ריבוע יורש ממלבן וממקבילית

avatar ענה intval ב 14 לפברואר 2013 #

זה מה שכתבת:

- מקבילית יורשת ממרובע
-- מלבן יורש ממקבילית
=> ריבוע יורש ממלבן וממקבילית

בפועל ריבוע לא יורש משני קלאסים, הוא יורש רק ממלבן
מלבן כבר יורש ממקבילית, לכן לריבוע כבר תהיה את כל הפונקציונאליות של מלבן
וגם את הפונקצינאליות של מקבילית שהוא קיבל דרך המלבן

avatar ענה OrelBeY ב 14 לפברואר 2013 #

שוב התבלבלתי. סליחה. התכוונתי שריבוע יורש ממלבן וממעוין. (ומדלתון גם אם אני לא טועה).

avatar ענה intval ב 14 לפברואר 2013 #

אז כן.
חוץ מהשאלה ההיפותטית של מה יורש ממה, יש לך משהו לעשות עם זה? איזה פונקצינאליות כלשהי?
בגלל שחוץ מהורשה בתכנות יש קונצפת שמעט מכירים אבל לדעתי הוא הרבה יותר טוב מהרושה הוא נקרא Composition
כאשר הוא דורש ממך להכיר את המונח Interface ולעבוד איתו.

מה זה אומר? שאפר שלוותר פה על הורשה לגמרי

interface IMalben
{
  public function getArea();
}

interface IMeuyan
{
  public function getArea();
}

class Rectangle implements IMalben, IMeuyan
{
  public function getArea()
  {
    return $this->w * $this->h;
  }
}



או בגרסה היותר פשוטה והפחות מבלבלת:
כשיש לך סוס שמתפקד גם בתור כלי תחבורה וגם בתור חיה:
interface ITransport
{
  public function Move($miles) ;
}

interface IAnimal
{
  public function Eat(Food $food);
}


class Tiger implements IAnimal
{
  public function Eat(Food $food)
  {
    echo 'Tigriss ate ', $food, 'Yammi';
  }
}

class Bycicle implements ITransport
{
  public function Move($miles)
  {
    echo 'You ride a bycicle for ', $miles, ' miles';
  }
]


class Horse implements ITransport, IAnimal
{
  public function Eat(Food $food)
  {
    echo 'EEEEEEHHHHHHHAAAAA ', $food, 'Yammi';
  }
 
  public function Move($miles)
  {
    echo 'Dio, Dio ', $miles, ' to go';
  }
}



class Human
{
  public function RideSomewhere(ITransport $transport)
  {
    $transport->Move(24);
  }
}



ועכשיו החלק המעניין, מה קורה אם אתה רוצה "לירוש" את הפונקציונאליות הקיימת ולהוסיף לה
ולא לממש מאפס שוב פעם - כאן לתמונה נכנס composition:

interface ITransport
{
  public function Move($miles) ;
}

interface IAnimal
{
  public function Eat(IFood $food);
}

interface IGrassEatingAnimal extends IAnimal{}

class GrassEatingAnimal implements IGrassEatingAnimal
{
  public function Eat(IFood $food)
  {
      if(!$food instanceOf GrassFood)
      {
        throw new \InvalidArgumentException
          ("This Animal eats only grass");
      }
  }
}

class Horse implements ITransport, IGrassEatingAnimal
{
  private $animal;
 
  public function __construct()
  {
    $this->animal = new \GreassEatingAnimal();
  }

  public function Eat(Food $food)
  {
    return $this->animal->Eat($food);
  }
 
  public function Move($miles)
  {
    echo 'Dio, Dio ', $miles, ' to go';
  }
 
  public function AddedFunction_Caress
  {
    echo 'hrrrhrr, Moaaaaaaaaaar';
  }
}


שבמקרה הזה בפועל אין הורשה, אלה כל הפעולות ששיכות לחייה מועברות פנימה לחייה שבפנים :)

avatar ענה intval ב 14 לפברואר 2013 #

אגב, קומפוזיציה זה הסיבה העיקרית למה מי שיודע לתכנת מבין ש traits כנראה יגרמו לקוד יותר גרוע מאשר יועילו.
למזלנו, להבין traits לא יותר מדי קל, לכן מתכנתים בינוניים נמנעים מהם ולא כותבים את הקוד הגרוע שהיה מצופה להתקבל
ואילו מתכנתים מנוסים לא רואים סיבה ממשית להשתמש ב traits

avatar ענה iiddaannyy ב 14 לפברואר 2013 #

לא מסכים בעניין של ה-trait.
יש מקרים שתצטרך להשתמש ב-trait.
כמו במקרה של סינגלטון. תוכל ליצור trait סינגלטון. וכל קלאס שהוא סינגלטון ישתמש ב-trait הזה.
לא תוכל להשתמש בקומפוזיציה או הורשה בשביל סינגלטון.

avatar ענה intval ב 14 לפברואר 2013 #

בעיקרון אתה גם לא תרצה ליצור סינגלטון אם אתה עובד נכון.
(סינגלטון זה שקר כזה של קוד גרוע :)

avatar ענה iiddaannyy ב 15 לפברואר 2013 #

במקום שכל מחלקה שצריכה להיות סינגלטון תכיל פעולת getInstance ומשתנה פרטי instance סטטיים, אפשר להכניס את זה ל-trait.

avatar ענה OrelBeY ב 15 לפברואר 2013 #

לא ממש הבנתי, אולי כדאי לכתוב על זה מאמר או משהו כזה. :-)
בכל מקרה, חשבתי על משהו. כמה הערות לפני זה: המתודה printProperties() לא מושלמת מבחינת מה שהיא מדפיסה (פיסוק וכדו'), זו רק הדגמה לרעיון עצמו. (והשמות של המחלקות הן בהגייה עברית רק לשם הנוחות. XD)

class Metzula {}

class Meruba extends Metzula
{
    public static $parents = array();
    public static $properties = array("MERUBA");
}

class Malben extends Metzula
{
    public static $parents = array('Meruba');
    public static $properties = array("MALBEN");
}

class Meuyan extends Metzula
{
    public static $parents = array('Meruba');
    public static $properties = array("MEUYAN");
}

class Ribua extends Metzula
{
    public static $parents = array('Malben', 'Meuyan');
    public static $properties = array("RIBUA");
   
    /*
    / The methods that use the properies
    / are going to use the $parents array.
    / The function below is an example for that.
    */

    public static function printProperties()
    {
        echo "Properties: ";
        foreach(self::$properties as $property)
        {
            echo $property.",";
        }
       
        foreach (self::$parents as $parent)
        {
            echo " Inherited from ".$parent.": ";
            $parent_properties = $parent::$properties;
            foreach ($parent_properties as $parent_property)
            {
                echo $parent_property.",";
            }
        }
    }
}

Ribua::printProperties();
// Echoes:
// Properties: RIBUA, Inherited from Malben: MALBEN, Inherited from Meuyan: MEUYAN,

רק עכשיו קלטתי שגם אם הייתה תמיכה ב-Multiple Inheritance הייתי צריך להשתמש בשיטה הזאת, אבל בכל זאת - הלוואי שהיה, כי זה הרבה יותר סמנטי.
אה, ובקשר לשאלה למה אני רוצה את זה - זה בשביל מבחן שלי במתמטיקה. XDDD

avatar ענה intval ב 15 לפברואר 2013 #

עכשיו תכתוב אותו דבר אבל בלי שום דבר סטטי

avatar ענה OrelBeY ב 15 לפברואר 2013 #

XDD
עוד לא הבנתי מה ההתנגדות שלך לסטטי, זה דווקא מאוד עוזר.